﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DomainDriver.DomainModeling.Repositories;
using System.Data.Entity;
using DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage;
using DomainDriver.DomainModeling.StorageManagers;

namespace DomainDriver.DomainModeling.DomainObjects
{
    public static class DomainObjectUtils
    {
        #region Detach for Repository Methods

        public static void DetachDomainObjectIfNecessary<KEY, KEYED_DOMAIN_OBJECT>(this IRepository<KEY, KEYED_DOMAIN_OBJECT> repository, KEYED_DOMAIN_OBJECT domainObject)
            where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            var domainObjects = new KEYED_DOMAIN_OBJECT[] { domainObject };
            DetachDomainObjectsIfNecessary(repository, domainObjects);
        }

        public static void DetachDomainObjectsIfNecessary<KEY, KEYED_DOMAIN_OBJECT>(this IRepository<KEY, KEYED_DOMAIN_OBJECT> repository, IEnumerable<KEYED_DOMAIN_OBJECT> domainObjects)
            where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            var dbContext = (repository.CurrentDataSource as DbContext);
            if (dbContext == null)
            { return; }

            DetachDomainObjects<KEY, KEYED_DOMAIN_OBJECT>(dbContext, domainObjects);
        }

        #endregion

        #region Detach for StorageManager Methods

        public static void DetachDomainObjectIfNecessary<KEY, KEYED_DOMAIN_OBJECT>(this IStorageManager<KEY, KEYED_DOMAIN_OBJECT> storageManager, KEYED_DOMAIN_OBJECT domainObject)
            where KEYED_DOMAIN_OBJECT : class, IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            var domainObjects = new KEYED_DOMAIN_OBJECT[] { domainObject };
            DetachDomainObjectsIfNecessary(storageManager, domainObjects);
        }

        public static void DetachDomainObjectsIfNecessary<KEY, KEYED_DOMAIN_OBJECT>(this IStorageManager<KEY, KEYED_DOMAIN_OBJECT> storageManager, IEnumerable<KEYED_DOMAIN_OBJECT> domainObjects)
            where KEYED_DOMAIN_OBJECT : class, IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            var dbContext = (storageManager.DataProvider.DataSource as DbContext);
            if (dbContext == null)
            { return; }

            DetachDomainObjects<KEY, KEYED_DOMAIN_OBJECT>(dbContext, domainObjects);
        }

        #endregion

        #region Detach for DbContext Methods

        public static void DetachDomainObject<KEY, KEYED_DOMAIN_OBJECT>(this DbContext dbContext, KEYED_DOMAIN_OBJECT domainObject)
            where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            var domainObjects = new KEYED_DOMAIN_OBJECT[] { domainObject };
            DetachDomainObjects<KEY, KEYED_DOMAIN_OBJECT>(dbContext, domainObjects);
        }

        public static void DetachDomainObjects<KEY, KEYED_DOMAIN_OBJECT>(this DbContext dbContext, IEnumerable<KEYED_DOMAIN_OBJECT> domainObjects)
            where KEYED_DOMAIN_OBJECT : IKeyedDomainObject<KEY, KEYED_DOMAIN_OBJECT>
        {
            if (dbContext == null)
            { return; }

            foreach (object domainObject in domainObjects)
            {
                dbContext.Entry(domainObject).State = EntityState.Detached;

                var domainObjectAsDetachableAggregate = (domainObject as IEfAggregate_Detachable<KEYED_DOMAIN_OBJECT>);
                if (domainObjectAsDetachableAggregate == null)
                { continue; }

                domainObjectAsDetachableAggregate.DetachNestedAggregateValues(dbContext);
            }
        }

        #endregion
    }
}